﻿using System;
using System.Linq;
using System.Threading.Tasks;
using System.Collections.Concurrent;
using System.Diagnostics;
using YmtSystem.CrossCutting;

namespace YmtAuth.AppService
{
    /// <summary>
    /// 收集IP区域数据的缓存
    /// </summary>
    /// <typeparam name="TKey"></typeparam>
    /// <typeparam name="TValue"></typeparam>
    public class IpAreaCache<TKey, TValue>
    {
        private static int limitCount = 0;
        private const int DefaultLimitCount = 500000;

        private static readonly ConcurrentDictionary<TKey, CacheItem> cache = new ConcurrentDictionary<TKey, CacheItem>();

        public static bool TryAdd(TKey key, TValue item)
        {
            bool added = false;
            
            RemoveExpiredCacheItem();

            if (!Exists(key))
            {
                AddCacheItem(key, item);
                added = true;
            }
            else
            {
                TValue existedCacheItem = GetItem(key);
                YmatouLoggingService.Debug("IP区域数据-存在未过期缓存; AddIpAreaCache, key: {0}, 创建时间: {1}, cache count: {2}", key, existedCacheItem, cache.Count);
            }

            return added;
        }

        internal static bool Exists(TKey key)
        {
            return cache.ContainsKey(key);
        }
        internal static TValue GetItem(TKey key)
        {
            CacheItem item = new CacheItem();

            if (cache.TryGetValue(key, out item))
            {
                if (item.ExpiredTime > DateTime.Now)
                    return item.Value;
                else
                    return default(TValue);
            }
            return item.Value;
        }
        internal static void RemoveItem(TKey key)
        {
            CacheItem item;

            YmtSystem.CrossCutting.YmatouLoggingService.Debug("IP区域数据-移除缓存; RemoveItem, key: {0}", key);
            cache.TryRemove(key, out item);
        }

        internal static void RemoveItemByValue(TValue value)
        {
            var keys = cache.Where(e => e.Value.Value.Equals(value)).AsParallel().Select(e => e.Key);
            if (keys.Any())
            {
                //foreach (var k in keys)
                Parallel.ForEach(keys, k =>
                    RemoveItem(k));
            }
        }

        private static void AddCacheItem(TKey key, TValue item)
        {
            try
            {
               YmatouLoggingService.Debug("IP区域数据-添加缓存; AddCacheItem, key: {0}, value: {1}", key, item);
                
                cache.TryAdd(key, new CacheItem { Value = item, ExpiredTime = DateTime.Now.Add(ExpiredTimeSpan) });
            }
            catch (Exception ex)
            {
                YmatouLoggingService.Error("IP区域数据-添加缓存异常 {0}", ex);
            }
        }


        private static void RemoveExpiredCacheItem()
        {
            var stopWatch = Stopwatch.StartNew();
            var keys = cache.Where(e => e.Value.ExpiredTime <= DateTime.Now).AsParallel().Select(e => e.Key);
            if (keys.Any())
            {
                //foreach (var k in keys)
                Parallel.ForEach(keys, k =>
                    RemoveItem(k));
            }
            
            if (cache.Count >= LimitCount)
            {
                YmatouLoggingService.Debug("IP区域数据-清空缓存; RemoveExpiredCacheItem, LimitCount: {0}, 实际LimitCount: {1}", LimitCount, cache.Count);
                cache.Clear();
            }
            stopWatch.Stop();
            if (stopWatch.ElapsedMilliseconds > 30)
                YmatouLoggingService.Debug("[IpAreaCache.RemoveExpiredCacheItem] run:{0:N0}", stopWatch.ElapsedMilliseconds);
        }

        private static int LimitCount { 
            
            get
            {
                if(limitCount <= 0)
                {
                    limitCount = System.Configuration.ConfigurationManager.AppSettings["IPAreaCacheLimit"] == null ? DefaultLimitCount : Convert.ToInt32(System.Configuration.ConfigurationManager.AppSettings["IPAreaCacheLimit"]); ;
                }
                return limitCount;
            }
        }

        private static TimeSpan? expiredTimeSpan = null;

        private static TimeSpan ExpiredTimeSpan
        {
            get
            {
                if (expiredTimeSpan == null)
                {
                    var exTimeSpan = System.Configuration.ConfigurationManager.AppSettings["IPAreaCacheTimeOut"];
                    if (string.IsNullOrEmpty(exTimeSpan))
                    {
                        expiredTimeSpan = TimeSpan.FromDays(1);
                    }
                    else
                    {
                        expiredTimeSpan = TimeSpan.Parse(exTimeSpan);
                    }

                    YmatouLoggingService.Debug("IP区域数据-缓存间隔, ExpiredTimeSpan: {0}", expiredTimeSpan.ToString());
                }

                return expiredTimeSpan.Value;
            }
        }

        internal struct CacheItem
        {
            public TValue Value { get; set; }
            public DateTime ExpiredTime { get; set; }
        }
    }
}
